package com.nulldev.util.analytics;

import java.io.File;
import java.util.ArrayList;
import java.util.LinkedList;

import com.nulldev.util.io.FileUtil;
import com.nulldev.util.io.MD5Utils;
import com.nulldev.util.java.JVMUtil;
import com.nulldev.util.net.IPUtil;
import com.nulldev.util.os.CPUUtil;
import com.nulldev.util.os.GPUUtil;
import com.nulldev.util.os.OSUtil;
import com.nulldev.util.os.OSUtil.OSType;
import com.nulldev.util.other.ArrayUtils;

public class nAnalytics {

	private final String instance_name;
	private OSType os_type = OSType.Other;
	private String os = "N/A";
	private String ip = "N/A";
	private String uuid = "N/A";
	private String gpu = "N/A";
	private String cpu = "N/A";
	private final LinkedList<Throwable> errors = new LinkedList<Throwable>();
	private String callClass;
	
	public nAnalytics(final String name) {
		try {
			this.callClass = Thread.currentThread().getStackTrace()[2].getClassName();
		}catch (Exception e) {
			this.callClass = this.getClass().getName();
		}
		this.instance_name = name;
		nAnalyticsSettings.register(this);
		this.query();
	}
	
	public void query() {
		this.os_type = OSUtil.getOperatingSystemType();
		if (nAnalyticsSettings.allowOSFull) {
			this.os = OSUtil.getOperatingSystemString();
		}else {
			this.os = "N/A";
		}
		if (nAnalyticsSettings.allowNetwork) {
			this.ip = IPUtil.getPublicIP();
		}else {
			this.ip = "N/A";
		}
		if (nAnalyticsSettings.allowGPU) {
			this.gpu = GPUUtil.parse().toString();
		}else {
			this.gpu = "N/A";
		}
		if (nAnalyticsSettings.allowCPU) {
			this.cpu = CPUUtil.parse().toString();
		}else {
			this.cpu = "N/A";
		}
		if (nAnalyticsSettings.allowUID) {
			final String[] args = new String[] {this.os_type.toString(), IPUtil.getInternetLocalIP_Safe(), this.cpu, this.gpu};
			this.uuid = MD5Utils.getMD5Hex(ArrayUtils.StringArrayUtils.addFrom(args, 0));
		}else {
			this.uuid = "N/A";
		}
	}
	
	@Override
	public String toString() {
		final StringBuilder out = new StringBuilder();
		out.append("OS Type: " + this.os_type + JVMUtil.LINE_SEP_WINDOWS);
		out.append("OS: " + this.os + JVMUtil.LINE_SEP_WINDOWS);
		out.append("IP: " + this.ip + JVMUtil.LINE_SEP_WINDOWS);
		out.append("CPU: " + this.cpu + JVMUtil.LINE_SEP_WINDOWS);
		out.append("GPU: " + this.gpu + JVMUtil.LINE_SEP_WINDOWS);
		out.append("UUID: " + this.uuid);
		return out.toString();
	}

	public String getName() {
		return this.instance_name;
	}

	public String toRList() {
		return this.instance_name + ", " + this.callClass;
	}
	
	public String[] getBasedErrors() {
		final ArrayList<String> out = new ArrayList<String>();
		for (final Throwable t : (LinkedList<Throwable>) this.errors.clone()) {
			out.add(JVMUtil.fromErrorToBase64(t));
		}
		return ArrayUtils.arrayListToArray(out);
	}
	
	public boolean registerError(final Throwable err) {
		return this.errors.add(err);
	}
	
	public void onErrorDump(final String[] data) {}
	
	public void trigger_data_dump() {
		this.onErrorDump(fetch_data_dump());
	}
	
	public String[] fetch_data_dump() {
		this.query();
		final ArrayList<String> data = new ArrayList<String>();
		data.add("nullUtil.nAnalytics.dump[0]" + JVMUtil.LINE_SEP_WINDOWS);
		data.add("======= Start Data Dump =======" + JVMUtil.LINE_SEP_WINDOWS);
		data.add("Triggered by: " + this.instance_name + JVMUtil.LINE_SEP_WINDOWS);
		data.add("Analytics created by: " + this.callClass + JVMUtil.LINE_SEP_WINDOWS);
		data.add("OS Type: " + this.os_type.toString() + JVMUtil.LINE_SEP_WINDOWS);
		data.add("OS: " + this.os + JVMUtil.LINE_SEP_WINDOWS);
		data.add("IP: " + this.ip + JVMUtil.LINE_SEP_WINDOWS);
		data.add("UID: " + this.uuid + JVMUtil.LINE_SEP_WINDOWS);
		data.add("CPU: " + this.cpu + JVMUtil.LINE_SEP_WINDOWS);
		data.add("GPU: " + this.gpu + JVMUtil.LINE_SEP_WINDOWS);
		if (getBasedErrors() != null) {
			data.add("Errors:" + JVMUtil.LINE_SEP_WINDOWS);
			for (final String err : getBasedErrors()) {
				data.add("- " + err.replaceAll(JVMUtil.LINE_SEP_WINDOWS, ""));
			}
		}
		data.add("======= End Data Dump =======");
		return ArrayUtils.arrayListToArray(data);
	}
	
	public void writeToFile(final File file) throws Exception {
		FileUtil.writeFile(file, ArrayUtils.toArrayList(fetch_data_dump()));
	}
}
